package p.ithorns.support.license.service;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import p.ithorns.framework.common.codec.RSACodec;
import p.ithorns.framework.common.model.Pair;
import p.ithorns.framework.common.codec.AESCodec;
import p.ithorns.framework.common.utils.JsonUtil;
import p.ithorns.framework.common.uuid.UuidUtil;
import p.ithorns.support.license.model.AuthReq;
import p.ithorns.support.license.model.License;
import p.ithorns.support.license.model.LicenseBody;
import p.ithorns.support.license.model.LicenseException;
import p.ithorns.support.license.model.entity.LicenseEntity;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Base64;
import java.util.Objects;

/**
 * GrantService
 *
 * @author Ht.L
 * @date 2023-12-13 09:50
 * @since 1.0.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class GrantService {

    private final LicenseService licService;

    /**
     * 获取授权文件
     *
     * @param req -授权参数
     * @return ResponseEntity
     */
    @Transactional
    public ResponseEntity<String> genAuthorize(@Validated AuthReq req) throws Exception {
        // 检查是否已存在该client的授权
        String clientId = req.getClientId();
        LicenseEntity entity = licService.getById(clientId);
        if (null == entity) {
            entity = new LicenseEntity();
            Pair<String, String> keyPair = null;
            try {
                keyPair = RSACodec.getKeyPair();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }

            entity.setPrivateKey(keyPair.getLeft());
            entity.setPublicKey(keyPair.getRight());

            entity.setCreateTime(LocalDateTime.now());
            log.info("【服务端】-> 生成RSA密钥对");
        }

        // 每次都产生随机码
        String authCode = UuidUtil.uuid(clientId);

        // 服务端使用authCode生成AES-Key
        String feed = Grantor.genAuthCode(clientId, authCode);
        log.info("【服务端】-> 生成feed: {}", feed);

        // 生成AES-Key
        SecretKey secretKey = AESCodec.generateKey(feed);
        String aesKey = AESCodec.encodeKey(secretKey);
        log.info("【服务端】-> 生成AES-Key: {}", aesKey);

        // Authorize auth = new Authorize(entity.getPublicKey(), authCode);
        entity.setClientId(clientId);
        entity.setAuthCode(authCode);
        entity.setFeed(feed);
        entity.setAesKey(aesKey);
        entity.setExpiration(req.getExpiration());

        // 更新DB
        entity.setUpdateTime(LocalDateTime.now());
        licService.saveOrUpdate(entity);

        // base64授权信息
        String auth = base64EncodeAuth(authCode, entity.getPublicKey());

        // 以clientId为文件名输出
        return ResponseEntity.ok()
                .contentType(MediaType.parseMediaType("application/octet-stream"))
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + clientId)
                .body(auth);
    }


    /**
     * 生成证书
     *
     * @param clientId -客户ID
     * @param authCode -授权码
     * @return LicenseBody -证书内容
     */
    public LicenseBody generateLicense(String clientId, String authCode) {
        LicenseEntity licenseEntity = licService.getById(clientId);
        String authCodeDb = licenseEntity.getAuthCode();
        if (!Objects.equals(authCode, authCodeDb)) {
            throw new LicenseException("非法的授权码");
        }

        String privateKey = licenseEntity.getPrivateKey();

        try {
            // 生成License
            License license = Grantor.genLicense(licenseEntity);
            log.info("【服务端】-> 生成Lic: {}", license);

            // 服务端使用AES-Key对证书加密
            SecretKey aesKey = AESCodec.decodeKey(licenseEntity.getAesKey());
            String json = Objects.requireNonNull(JsonUtil.toJson(license));
            String aesEncrypt = AESCodec.encrypt(json, aesKey);
            log.info("【服务端】-> AES加密Lic: {}", aesEncrypt);

            // 对AES加密内容进行签名
            String rsaSign = RSACodec.sign(aesEncrypt, privateKey);
            log.info("【服务端】-> RSA私钥签名: {}", rsaSign);

            // 使用私钥对AES加密内容进行RSA加密(客户端需要使用公钥解密，并验证AES内容是否被篡改)
            String rsaEncrypt = RSACodec.encryptPrivate(aesEncrypt, privateKey);
            log.info("【服务端】-> RSA私钥加密Lic: {}", rsaEncrypt);

            LicenseBody lb = new LicenseBody(rsaEncrypt, rsaSign);
            log.info("【服务端】-> 响应签名内容给客户端: {}", lb);

            // 更新证书内容
            licService.updateById(licenseEntity);

            // 响应
            return lb;
        } catch (Exception e) {
            throw new LicenseException(e);
        }
    }

    /**
     * base64授权内容
     * 格式: AuthCode@PublicKey
     *
     * @param publicKey -
     * @param authCode  -
     * @return String base64
     */
    private String base64EncodeAuth(String authCode, String publicKey) {
        byte[] bytes = (authCode + "@" + publicKey).getBytes(StandardCharsets.UTF_8);
        return Base64.getEncoder().encodeToString(bytes);
    }

}